package com.uinnova.product.eam.service.impl;

import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.framework.exception.ServiceException;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.comm.model.VcDiagramDir;
import com.uinnova.product.eam.model.AppStrategyDto;
import com.uinnova.product.eam.service.*;
import com.uinnova.product.eam.service.asset.BmConfigSvc;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uino.api.client.cmdb.ICIClassApiSvc;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.bean.permission.base.SysUser;
import com.uino.service.cmdb.microservice.IRltClassSvc;
import com.uino.util.sys.SysUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;

/**
 * 应用架构的相关服务
 *
 * @author yuhao.guan
 * @version 2020/7/28
 */
@Service
public class AppArchitectureSvcImpl implements IAppArchitectureSvc {

    public static final String APP_SYSTEM_TACTICS_CL_ASS_CODE = "APP_SYSTEM_TACTICS_ClASS_CODE";
    public static final String APP_SYSTEM_TACTICS_RLT_CLASS_CODE = "APP_SYSTEM_TACTICS_RLT_CLASS_CODE";
    private static Logger logger = LoggerFactory.getLogger(AppArchitectureSvcImpl.class);

    @Autowired
    private ICIRltSwitchSvc ciRltSvc;

    @Autowired
    private IRltClassSvc rltClassSvc;

    @Autowired
    private IEamCiSvc eamCiSvc;

    @Autowired
    private ICISwitchSvc iciSwitchSvc;

    @Autowired
    private VcDiagramSvc diagramSvc;

    @Resource
    private ICIClassApiSvc iciClassApiSvc;

    @Resource
    private BmConfigSvc bmConfigSvc;

    @Override
    public List<AppStrategyDto> getAppStrategy(LibType libType) {
        List<AppStrategyDto> retList = new ArrayList<>();
        // 获取所有应用
        String classCode = bmConfigSvc.getConfigType(APP_SYSTEM_TACTICS_CL_ASS_CODE);
        if (BinaryUtils.isEmpty(classCode)) {
            return retList;
        }
        List<CcCiInfo> appList = eamCiSvc.getCiInfoListByClassCode(classCode, libType);
        if (!BinaryUtils.isEmpty(appList)) {
            // 获取应用关联的应用数量
            Map<Long, Integer> appRltCount = this.getAppRltCount(libType);
            for (CcCiInfo appInfo : appList) {
                AppStrategyDto appStrategyDto = new AppStrategyDto();
                appStrategyDto.setBusinessFit(appInfo.getAttrs().get("业务适配度"));
                appStrategyDto.setTechnicalFit(appInfo.getAttrs().get("技术适配度"));
                appStrategyDto.setCi(appInfo.getCi());
                appStrategyDto.setRltCount(appRltCount.getOrDefault(appInfo.getCi().getId(), 0));
                retList.add(appStrategyDto);
            }
        }
        return retList;
    }

    @Override
    public HashMap<String, Object> getStaffMatrixView(LibType libType) {
        String classCode = bmConfigSvc.getConfigType(APP_SYSTEM_TACTICS_CL_ASS_CODE);
        if (BinaryUtils.isEmpty(classCode)) {
            throw new BinaryException("应用系统整体分类信息未配置，请在视图配置中添加");
        }
        List<CcCiInfo> ccCiInfoList = eamCiSvc.getCiInfoListByClassCode(classCode, libType);
        return parseStaffMatrixData(ccCiInfoList);
    }

    @Override
    public HashMap<String, Object> getStrategyMatrixView(LibType libType) {
        String classCode = bmConfigSvc.getConfigType(APP_SYSTEM_TACTICS_CL_ASS_CODE);
        if (BinaryUtils.isEmpty(classCode)) {
            throw new BinaryException("应用系统整体分类信息未配置，请在视图配置中添加");
        }
        List<CcCiInfo> ccCiInfoList = eamCiSvc.getCiInfoListByClassCode(classCode, libType);
        return parseStrategyMatrixData(ccCiInfoList);
    }

    @Override
    public List<CcCiInfo> getTimeAxisView(LibType libType) {
        // 获取配置分类Code
        String confJson = bmConfigSvc.getConfigType(APP_SYSTEM_TACTICS_CL_ASS_CODE);
        if (BinaryUtils.isEmpty(confJson)) {
            return Collections.emptyList();
        }
        List<CcCiInfo> result = eamCiSvc.getCiInfoListByClassCode(confJson, libType);
        return result;
    }

    @Override
    public Long saveOrUpdateAppInfo(CcCiInfo ciInfo) {
        if (ciInfo == null) {
            throw new ServiceException("应用系统信息不能为空!");
        }
        if (CollectionUtils.isEmpty(ciInfo.getAttrs())) {
            throw new ServiceException("应用系统配置参数不能为空!");
        }
        if (StringUtils.isEmpty(ciInfo.getAttrs().get("应用系统中文名称"))) {
            throw new ServiceException("应用系统中文名称不能为空!");
        }
        if (StringUtils.isEmpty(ciInfo.getAttrs().get("应用系统编号"))) {
            throw new ServiceException("应用系统编号不能为空!");
        }
        if (StringUtils.isEmpty(ciInfo.getAttrs().get("应用系统分类"))) {
            throw new ServiceException("应用系统分类不能为空!");
        }
        String classCode = bmConfigSvc.getConfigType(APP_SYSTEM_TACTICS_CL_ASS_CODE);
        if (BinaryUtils.isEmpty(classCode)) {
            throw new BinaryException("应用系统整体分类信息未配置，请在视图配置中添加");
        }
        Long ciClassId = getCiClassIdByCode(classCode);
        CcCi ci = new CcCi();
        ci.setClassId(ciClassId);
        ciInfo.setCi(ci);
        // 创建架构设计应用对应文件夹
        SysUser loginUser = SysUtil.getCurrentUserInfo();
        Long userId = loginUser.getId();
        Long domainId = loginUser.getDomainId();
        VcDiagramDir dir = new VcDiagramDir();
        dir.setDirName(ciInfo.getAttrs().get("应用系统中文名称"));
        dir.setDirType(2);
        dir.setParentId(1L);
        if (BinaryUtils.isEmpty(dir.getUserId())) {
            dir.setUserId(userId);
        }
        dir.setDomainId(domainId);
        dir.setSubjectId(0L);
        dir.setDirInit(0);
        dir.setSysType(ciInfo.getAttrs().get("应用系统分类"));
        Long id = iciSwitchSvc.saveOrUpdateCI(ciInfo, LibType.BASELINE);
        CcCiInfo ciInfoById = iciSwitchSvc.getCiInfoById(id, LibType.BASELINE);
        dir.setEsSysId(ciInfoById.getCi().getCiCode());
        Long dirId = diagramSvc.saveOrUpdateDiagramDir(domainId, dir);
        this.saveAppDirs(dirId, domainId, userId);
        return id;
    }

    private Long getCiClassIdByCode(String ciClassCode) {
        CCcCiClass cCcCiClass = new CCcCiClass();
        cCcCiClass.setClassCodeEqual(ciClassCode);
        List<CcCiClassInfo> ccCiClassInfos = iciClassApiSvc.queryClassByCdt(cCcCiClass);
        return ccCiClassInfos.get(0).getCiClass().getId();
    }

    private void saveAppDirs(Long parentId, Long domainId, Long userId){
        List<String> dirNames = Arrays.asList("基本信息","业务架构", "数据架构", "应用架构", "技术架构");
        for(String name : dirNames){
            VcDiagramDir dir = new VcDiagramDir();
            dir.setParentId(parentId);
            dir.setDirName(name);
            dir.setDirType(2);
            dir.setUserId(userId);
            dir.setDomainId(domainId);
            dir.setSubjectId(0L);
            dir.setDirInit(0);
            diagramSvc.saveOrUpdateDiagramDir(domainId, dir);
        }
    }

    /**
     * 解析策略矩阵图
     *
     * @param ccCiInfoList ci信息数据集
     * @return 解析结果
     */
    private HashMap<String, Object> parseStrategyMatrixData(List<CcCiInfo> ccCiInfoList) {
        LinkedHashSet<String> values = new LinkedHashSet<>();
        HashMap<String, Map<String, List<CcCiInfo>>> map = new HashMap<>(30);
        for (CcCiInfo ccCiInfo : ccCiInfoList) {
            Map<String, String> attrs = ccCiInfo.getAttrs();
            String capability = attrs.get("所属业务能力");
            if (StringUtils.isBlank(capability)) {
                capability = "未指定所属业务能力";
            }
            values.add(capability);
            double technologyFitness = Double.parseDouble(attrs.get("技术适配度"));
            double businessFitness = Double.parseDouble(attrs.get("业务适配度"));
            //计算在哪一个象限中
            String quadrant;
            if (technologyFitness < 3) {
                if (businessFitness < 3) {
                    quadrant = "消除（Eliminate）";
                } else {
                    quadrant = "迁移（Migrate）";
                }
            } else {
                if (businessFitness < 3) {
                    quadrant = "容忍（Tolerate）";
                } else {
                    quadrant = "投资（Invest）";
                }
            }
            Map<String, List<CcCiInfo>> stringListMap = map.get(quadrant);
            if (stringListMap == null) {
                stringListMap = new LinkedHashMap<>();
            }
            List<CcCiInfo> ccCis = stringListMap.get(capability);
            if (ccCis == null) {
                ccCis = new ArrayList<>();
            }
            ccCis.add(ccCiInfo);
            stringListMap.put(capability, ccCis);
            map.put(quadrant, stringListMap);
        }
        //将未指定所属业务能力添加到最后
        if (values.contains("未指定所属业务能力")) {
            values.remove("未指定所属业务能力");
            values.add("未指定所属业务能力");
        }
        HashMap<String, Object> resultMap = new HashMap<>(2);
        resultMap.put("所属业务能力", values);
        //将map数据转换成集合，方便前端解析数据
        ArrayList<HashMap<String, Map<String, List<CcCiInfo>>>> lists = new ArrayList<>();
        for (String key : map.keySet()) {
            HashMap<String, Map<String, List<CcCiInfo>>> stringMapHashMap = new HashMap<>();
            stringMapHashMap.put(key, map.get(key));
            lists.add(stringMapHashMap);
        }
        resultMap.put("象限", lists);
        return resultMap;
    }

    /**
     * 解析人员矩阵数据
     *
     * @param ccCiInfoList ci数据集合
     * @return 解析结果
     */
    private HashMap<String, Object> parseStaffMatrixData(List<CcCiInfo> ccCiInfoList) {
        LinkedHashSet<String> values = new LinkedHashSet<>();
        Map<String, Map<String, List<CcCiInfo>>> map = new LinkedHashMap<>(30);
        for (CcCiInfo ccCiInfo : ccCiInfoList) {
            Map<String, String> attrs = ccCiInfo.getAttrs();
            String capability = attrs.get("所属业务能力");
            if (StringUtils.isBlank(capability)) {
                capability = "未指定所属业务能力";
            }
            values.add(capability);
            String author = attrs.get("管理员");
            if (StringUtils.isBlank(author)) {
                author = "未指定管理员";
            }
            Map<String, List<CcCiInfo>> stringListMap = map.get(author);
            if (stringListMap == null) {
                stringListMap = new LinkedHashMap<>();
            }
            List<CcCiInfo> ccCis = stringListMap.get(capability);
            if (ccCis == null) {
                ccCis = new ArrayList<>();
            }
            ccCis.add(ccCiInfo);
            stringListMap.put(capability, ccCis);
            map.put(author, stringListMap);
        }
        //将未指定所属业务能力添加到最后
        if (values.contains("未指定所属业务能力")) {
            values.remove("未指定所属业务能力");
            values.add("未指定所属业务能力");
        }
        //将未指定管理员放到最后
        Map<String, List<CcCiInfo>> map1 = map.get("未指定管理员");
        if (map1 != null) {
            map.remove("未指定管理员");
            map.put("未指定管理员", map1);
        }
        HashMap<String, Object> resultMap = new HashMap<>(2);
        resultMap.put("所属业务能力", values);
        //将map数据转换成集合，方便前端解析数据
        ArrayList<HashMap<String, Map<String, List<CcCiInfo>>>> lists = new ArrayList<>();
        for (String key : map.keySet()) {
            HashMap<String, Map<String, List<CcCiInfo>>> stringMapHashMap = new HashMap<>();
            stringMapHashMap.put(key, map.get(key));
            lists.add(stringMapHashMap);
        }
        resultMap.put("管理员", lists);
        return resultMap;
    }


    /**
     * 获取应用访问应用的关系列表
     *
     * @param libType 库类型
     */
    public List<CcCiRltInfo> getAppRltList(LibType libType) {
        CCcCiClass cdt = new CCcCiClass();
        String rltClassCode = bmConfigSvc.getConfigType(APP_SYSTEM_TACTICS_RLT_CLASS_CODE);
        if (BinaryUtils.isEmpty(rltClassCode)) {
            throw new BinaryException("应用系统关系分类信息未配置，请在视图配置中添加");
        }
        cdt.setClassCodeEqual(rltClassCode);
        List<CcCiClassInfo> rltClassList = rltClassSvc.getRltClassByCdt(cdt);
        if (!BinaryUtils.isEmpty(rltClassList)) {
            long classId = rltClassList.get(0).getCiClass().getId();
            ESRltSearchBean searchBean = new ESRltSearchBean();
            // TODO 如果关系超过5000条需调整
            searchBean.setPageSize(5000);
            List<Long> classIdList = new ArrayList<>();
            classIdList.add(classId);
            searchBean.setRltClassIds(classIdList);
            Page<CcCiRltInfo> rltPage = ciRltSvc.searchRltByBean(searchBean, libType);
            return rltPage.getData();
        } else {
            logger.error("The application relation could not be found by the configured relation class name");
            return new ArrayList<>();
        }
    }


    /**
     * 获取每个应用通过关系(应用访问应用)关联的应用的数量
     *
     * @param libType 库类型
     * @return 应用关联关系的数量 Map<应用ID, 关联关系数量>
     */
    private Map<Long, Integer> getAppRltCount(LibType libType) {
        Map<Long, Integer> retMap = new HashMap<>();
        List<CcCiRltInfo> rltList = getAppRltList(libType);
        for (CcCiRltInfo rltInfo : rltList) {
            // 源CI是此应用的，关联数量+1
            if (retMap.containsKey(rltInfo.getSourceCiInfo().getCi().getId())) {
                int count = retMap.get(rltInfo.getSourceCiInfo().getCi().getId());
                retMap.put(rltInfo.getSourceCiInfo().getCi().getId(), count + 1);
            } else {
                retMap.put(rltInfo.getSourceCiInfo().getCi().getId(), 1);
            }

            // 目标CI中是此应用的，关联数量+1
            if (retMap.containsKey(rltInfo.getTargetCiInfo().getCi().getId())) {
                int count = retMap.get(rltInfo.getTargetCiInfo().getCi().getId());
                retMap.put(rltInfo.getTargetCiInfo().getCi().getId(), count + 1);
            } else {
                retMap.put(rltInfo.getTargetCiInfo().getCi().getId(), 1);
            }
        }
        return retMap;
    }

}
